<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>字数与推荐数关系 - 纵横小说推荐榜数据可视化平台</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/chart-styles.css') }}">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
</head>
<body>
    <!-- 顶部区域 -->
    <header class="header">
        <img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo" class="logo">
        <h1 class="system-title">纵横小说推荐榜数据可视化平台</h1>
    </header>

    <!-- 左侧导航栏 -->
    <nav class="sidebar">
        <a href="/" class="nav-item">
            <i class="fas fa-book-open fa-fw"></i>
            <span>图书列表</span>
        </a>
        <a href="/charts" class="nav-item">
            <i class="fas fa-chart-pie fa-fw"></i>
            <span>书籍类别分布</span>
        </a>
        <a href="/bar-chart" class="nav-item">
            <i class="fas fa-chart-bar fa-fw"></i>
            <span>类别点击量分析</span>
        </a>
        <a href="/line-chart" class="nav-item active">
            <i class="fas fa-chart-line fa-fw"></i>
            <span>字数与推荐数关系</span>
        </a>
        <a href="/boxplot" class="nav-item">
            <i class="fas fa-chart-simple fa-fw"></i>
            <span>状态与周推荐数</span>
        </a>
        <a href="/ranking-chart" class="nav-item">
            <i class="fas fa-trophy fa-fw"></i>
            <span>书籍点击量排名</span>
        </a>
    </nav>

    <!-- 主内容区 -->
    <main class="main-content">
        <h2 class="page-title">字数与推荐数关系</h2>

        <div class="chart-container">
            <div class="chart-header">
                <h3 class="chart-title">
                    <i class="fas fa-chart-line"></i>
                    字数与推荐数散点图
                </h3>
                <div class="chart-actions">
                    <div class="chart-filter">
                        <label for="categoryFilter">类别筛选：</label>
                        <select id="categoryFilter" class="filter-select">
                            <option value="all">全部类别</option>
                            <!-- 类别选项将通过JavaScript动态填充 -->
                        </select>
                    </div>
                    <button id="downloadBtn" class="action-button">
                        <i class="fas fa-download"></i>
                        <span>下载图表</span>
                    </button>
                    <button id="refreshBtn" class="action-button">
                        <i class="fas fa-sync-alt"></i>
                        <span>刷新数据</span>
                    </button>
                </div>
            </div>

            <div class="chart-description">
                <i class="fas fa-info-circle"></i>
                <p>此图表展示了纵横小说推荐榜中书籍总字数与总推荐数之间的关系，每个点代表一本书，可以通过类别筛选查看不同类型小说的数据分布。</p>
            </div>

            <div id="loadingIndicator" class="loading">
                <div class="loading-spinner"></div>
            </div>

            <div id="chart" class="chart"></div>
        </div>

        <div class="chart-container">
            <div class="chart-header">
                <h3 class="chart-title">
                    <i class="fas fa-chart-line"></i>
                    字数与推荐数趋势线
                </h3>
            </div>

            <div class="chart-description">
                <i class="fas fa-info-circle"></i>
                <p>此图表展示了不同字数区间的书籍平均推荐数趋势，帮助分析字数增长是否与推荐数呈现正相关关系。</p>
            </div>

            <div id="loadingIndicator2" class="loading">
                <div class="loading-spinner"></div>
            </div>

            <div id="trendChart" class="chart"></div>
        </div>

        <div class="chart-container">
            <div class="chart-header">
                <h3 class="chart-title">
                    <i class="fas fa-table"></i>
                    数据统计
                </h3>
            </div>

            <div class="stats-grid">
                <div class="stats-card">
                    <div class="stats-icon">
                        <i class="fas fa-calculator"></i>
                    </div>
                    <div class="stats-content">
                        <div class="stats-title">相关系数</div>
                        <div class="stats-value" id="correlationValue">计算中...</div>
                        <div class="stats-desc">字数与推荐数的皮尔逊相关系数</div>
                    </div>
                </div>

                <div class="stats-card">
                    <div class="stats-icon">
                        <i class="fas fa-book"></i>
                    </div>
                    <div class="stats-content">
                        <div class="stats-title">平均字数</div>
                        <div class="stats-value" id="avgWordsValue">计算中...</div>
                        <div class="stats-desc">所有书籍的平均字数(万)</div>
                    </div>
                </div>

                <div class="stats-card">
                    <div class="stats-icon">
                        <i class="fas fa-thumbs-up"></i>
                    </div>
                    <div class="stats-content">
                        <div class="stats-title">平均推荐数</div>
                        <div class="stats-value" id="avgRecsValue">计算中...</div>
                        <div class="stats-desc">所有书籍的平均推荐数(万)</div>
                    </div>
                </div>

                <div class="stats-card">
                    <div class="stats-icon">
                        <i class="fas fa-star"></i>
                    </div>
                    <div class="stats-content">
                        <div class="stats-title">推荐/字数比</div>
                        <div class="stats-value" id="ratioValue">计算中...</div>
                        <div class="stats-desc">平均每万字获得的推荐数</div>
                    </div>
                </div>
            </div>
        </div>
    </main>

    <script>
        // 初始化ECharts实例
        var chartDom = document.getElementById('chart');
        var myChart = echarts.init(chartDom);

        var trendChartDom = document.getElementById('trendChart');
        var trendChart = echarts.init(trendChartDom);

        var rawData = [];
        var categories = [];

        // 显示加载状态
        function showLoading(id = 'loadingIndicator') {
            document.getElementById(id).style.display = 'flex';
        }

        // 隐藏加载状态
        function hideLoading(id = 'loadingIndicator') {
            document.getElementById(id).style.display = 'none';
        }

        // 获取图表数据
        async function fetchChartData() {
            showLoading();
            showLoading('loadingIndicator2');

            try {
                console.log("开始获取数据...");
                const response = await fetch('/api/words-recommendations');
                if (!response.ok) {
                    throw new Error(`网络请求失败: ${response.status} ${response.statusText}`);
                }

                const data = await response.json();
                console.log("获取到的原始数据:", data);

                // 验证数据格式
                if (!Array.isArray(data)) {
                    throw new Error(`API返回的数据不是数组: ${typeof data}`);
                }

                if (data.length === 0) {
                    console.warn("API返回的数据数组为空");
                }

                // 验证数据字段
                const validData = data.filter(item => {
                    const hasRequiredFields = 
                        item && 
                        typeof item === 'object' && 
                        'book_name' in item && 
                        'category' in item && 
                        'total_words' in item && 
                        'total_recommendations' in item;
                    
                    if (!hasRequiredFields) {
                        console.warn("发现无效数据项:", item);
                        return false;
                    }
                    
                    // 确保数值字段是数字
                    item.total_words = parseFloat(item.total_words);
                    item.total_recommendations = parseFloat(item.total_recommendations);
                    
                    if (isNaN(item.total_words) || isNaN(item.total_recommendations)) {
                        console.warn("数据项包含非数字值:", item);
                        return false;
                    }
                    
                    return true;
                });

                console.log(`有效数据项: ${validData.length}/${data.length}`);
                rawData = validData;

                if (rawData.length === 0) {
                    throw new Error("没有有效的数据可以显示");
                }

                // 提取所有类别
                categories = [...new Set(rawData.map(item => item.category))];
                console.log("提取的类别:", categories);

                // 填充类别筛选器
                const categoryFilter = document.getElementById('categoryFilter');
                categoryFilter.innerHTML = '<option value="all">全部类别</option>';

                categories.forEach(category => {
                    const option = document.createElement('option');
                    option.value = category;
                    option.textContent = category;
                    categoryFilter.appendChild(option);
                });

                console.log("开始渲染散点图...");
                renderScatterChart('all');
                console.log("开始渲染趋势图...");
                renderTrendChart();
                console.log("开始计算统计数据...");
                calculateStatistics('all');
                console.log("数据处理和渲染完成");
            } catch (error) {
                console.error('获取或处理数据失败:', error);
                myChart.showLoading({
                    text: `数据加载失败: ${error.message}`,
                    maskColor: 'rgba(255, 255, 255, 0.8)',
                    textColor: '#ff4d4f'
                });
                trendChart.showLoading({
                    text: `数据加载失败: ${error.message}`,
                    maskColor: 'rgba(255, 255, 255, 0.8)',
                    textColor: '#ff4d4f'
                });
                
                // 显示错误信息在统计卡片中
                document.getElementById('correlationValue').textContent = 'Error';
                document.getElementById('avgWordsValue').textContent = 'Error';
                document.getElementById('avgRecsValue').textContent = 'Error';
                document.getElementById('ratioValue').textContent = 'Error';
            } finally {
                hideLoading();
                hideLoading('loadingIndicator2');
            }
        }

        // 线性回归计算
        function linearRegression(x, y) {
            const n = x.length;
            let sumX = 0;
            let sumY = 0;
            let sumXY = 0;
            let sumXX = 0;

            for (let i = 0; i < n; i++) {
                sumX += x[i];
                sumY += y[i];
                sumXY += x[i] * y[i];
                sumXX += x[i] * x[i];
            }

            const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
            const intercept = (sumY - slope * sumX) / n;

            return { slope, intercept };
        }

        // 计算皮尔逊相关系数
        function calculateCorrelation(x, y) {
            try {
                const n = x.length;
                
                // 检查数据长度
                if (n < 2 || n !== y.length) {
                    console.error("数据长度不足或不匹配，无法计算相关系数");
                    return NaN;
                }
                
                let sumX = 0;
                let sumY = 0;
                let sumXY = 0;
                let sumXX = 0;
                let sumYY = 0;

                for (let i = 0; i < n; i++) {
                    // 确保数据是有效的数字
                    if (isNaN(x[i]) || isNaN(y[i])) {
                        console.error("数据包含非数字值，无法计算相关系数");
                        return NaN;
                    }
                    
                    sumX += x[i];
                    sumY += y[i];
                    sumXY += x[i] * y[i];
                    sumXX += x[i] * x[i];
                    sumYY += y[i] * y[i];
                }

                const numerator = n * sumXY - sumX * sumY;
                const denominator = Math.sqrt((n * sumXX - sumX * sumX) * (n * sumYY - sumY * sumY));

                // 避免除以零
                if (denominator === 0) {
                    console.error("相关系数计算中分母为零");
                    return NaN;
                }

                return numerator / denominator;
            } catch (error) {
                console.error("计算相关系数时出错:", error);
                return NaN;
            }
        }

        // 渲染散点图
        function renderScatterChart(category) {
            // 筛选数据
            const filteredData = category === 'all'
                ? rawData
                : rawData.filter(item => item.category === category);

            // 准备数据
            const scatterData = filteredData.map(item => [
                item.total_words,
                item.total_recommendations,
                item.book_name,
                item.category
            ]);

            // 计算回归线
            let trendLineData = [];
            if (filteredData.length > 1) {
                const xValues = filteredData.map(item => item.total_words);
                const yValues = filteredData.map(item => item.total_recommendations);

                const { slope, intercept } = linearRegression(xValues, yValues);

                const minX = Math.min(...xValues);
                const maxX = Math.max(...xValues);

                trendLineData = [
                    [minX, minX * slope + intercept],
                    [maxX, maxX * slope + intercept]
                ];
            }

            // 配置选项
            const option = {
                title: {
                    text: '字数与推荐数关系散点图',
                    left: 'center'
                },
                tooltip: {
                    trigger: 'item',
                    formatter: function(params) {
                        return `书名: ${params.data[2]}<br/>` +
                               `类别: ${params.data[3]}<br/>` +
                               `总字数: ${params.data[0]} 万<br/>` +
                               `总推荐: ${params.data[1]} 万`;
                    }
                },
                xAxis: {
                    type: 'value',
                    name: '总字数(万)',
                    nameLocation: 'middle',
                    nameGap: 30,
                    splitLine: {
                        lineStyle: {
                            type: 'dashed'
                        }
                    }
                },
                yAxis: {
                    type: 'value',
                    name: '总推荐数(万)',
                    nameLocation: 'middle',
                    nameGap: 30,
                    splitLine: {
                        lineStyle: {
                            type: 'dashed'
                        }
                    }
                },
                series: [
                    {
                        name: '书籍数据',
                        type: 'scatter',
                        data: scatterData,
                        symbolSize: function(data) {
                            // 根据推荐数调整点的大小
                            return Math.sqrt(data[1]) * 1.5 + 5;
                        },
                        itemStyle: {
                            color: function(params) {
                                // 根据类别分配颜色
                                const colorList = [
                                    '#1890ff', '#13c2c2', '#52c41a', '#faad14', '#f5222d',
                                    '#722ed1', '#eb2f96', '#fa8c16', '#a0d911', '#fadb14',
                                    '#2f54eb', '#fa541c', '#391085', '#9254de', '#36cfc9'
                                ];

                                if (category === 'all') {
                                    const categoryIndex = categories.indexOf(params.data[3]);
                                    return colorList[categoryIndex % colorList.length];
                                } else {
                                    return '#1890ff';
                                }
                            }
                        }
                    },
                    {
                        name: '趋势线',
                        type: 'line',
                        data: trendLineData,
                        showSymbol: false,
                        lineStyle: {
                            color: '#ff4d4f',
                            width: 2,
                            type: 'dashed'
                        }
                    }
                ]
            };

            myChart.setOption(option);
        }

        // 渲染趋势图
        function renderTrendChart() {
            // 检查数据是否为空
            if (!rawData || rawData.length === 0) {
                console.error("没有可用的数据来渲染趋势图");
                return;
            }

            // 按字数分组
            const groups = [
                { min: 0, max: 100, label: '0-100万字' },
                { min: 100, max: 200, label: '100-200万字' },
                { min: 200, max: 300, label: '200-300万字' },
                { min: 300, max: 400, label: '300-400万字' },
                { min: 400, max: 500, label: '400-500万字' },
                { min: 500, max: Infinity, label: '500万字以上' }
            ];

            const groupData = groups.map(group => {
                const books = rawData.filter(item => 
                    item.total_words >= group.min && item.total_words < group.max
                );
                
                if (books.length === 0) return { 
                    label: group.label, 
                    avgWords: 0, 
                    avgRecs: 0, 
                    count: 0 
                };
                
                const totalWords = books.reduce((sum, book) => sum + parseFloat(book.total_words || 0), 0);
                const totalRecs = books.reduce((sum, book) => sum + parseFloat(book.total_recommendations || 0), 0);
                
                return {
                    label: group.label,
                    avgWords: totalWords / books.length,
                    avgRecs: totalRecs / books.length,
                    count: books.length
                };
            }).filter(group => group.count > 0);

            // 如果没有有效的分组数据，显示提示信息
            if (groupData.length === 0) {
                console.error("没有足够的数据来生成趋势图");
                trendChart.setOption({
                    title: {
                        text: '没有足够的数据来生成趋势图',
                        left: 'center'
                    }
                });
                return;
            }

            // 配置选项
            const option = {
                title: {
                    text: '字数区间与平均推荐数趋势',
                    left: 'center'
                },
                tooltip: {
                    trigger: 'axis',
                    formatter: function(params) {
                        const data = params[0].data;
                        return `${params[0].name}<br/>` +
                               `平均字数: ${data.avgWords.toFixed(2)} 万<br/>` +
                               `平均推荐数: ${data.avgRecs.toFixed(2)} 万<br/>` +
                               `书籍数量: ${data.count} 本`;
                    }
                },
                xAxis: {
                    type: 'category',
                    data: groupData.map(item => item.label),
                    axisLabel: {
                        interval: 0,
                        rotate: 30
                    }
                },
                yAxis: {
                    type: 'value',
                    name: '平均推荐数(万)',
                    nameLocation: 'middle',
                    nameGap: 30
                },
                series: [
                    {
                        name: '平均推荐数',
                        type: 'line',
                        data: groupData.map(item => ({
                            value: item.avgRecs,
                            avgWords: item.avgWords,
                            count: item.count
                        })),
                        smooth: true,
                        symbolSize: 8,
                        lineStyle: {
                            width: 3
                        },
                        itemStyle: {
                            color: '#1890ff'
                        },
                        areaStyle: {
                            color: {
                                type: 'linear',
                                x: 0,
                                y: 0,
                                x2: 0,
                                y2: 1,
                                colorStops: [{
                                    offset: 0, color: 'rgba(24,144,255,0.3)'
                                }, {
                                    offset: 1, color: 'rgba(24,144,255,0.1)'
                                }]
                            }
                        }
                    }
                ]
            };

            trendChart.setOption(option);
        }

        // 计算统计数据
        function calculateStatistics(category) {
            // 检查数据是否为空
            if (!rawData || rawData.length === 0) {
                console.error("没有可用的数据来计算统计信息");
                document.getElementById('correlationValue').textContent = 'N/A';
                document.getElementById('avgWordsValue').textContent = 'N/A';
                document.getElementById('avgRecsValue').textContent = 'N/A';
                document.getElementById('ratioValue').textContent = 'N/A';
                return;
            }

            // 筛选数据
            const filteredData = category === 'all'
                ? rawData
                : rawData.filter(item => item.category === category);

            if (filteredData.length === 0) {
                document.getElementById('correlationValue').textContent = 'N/A';
                document.getElementById('avgWordsValue').textContent = 'N/A';
                document.getElementById('avgRecsValue').textContent = 'N/A';
                document.getElementById('ratioValue').textContent = 'N/A';
                return;
            }

            try {
                // 计算平均值，确保数据是数值类型
                const totalWords = filteredData.reduce((sum, book) => sum + parseFloat(book.total_words || 0), 0);
                const totalRecs = filteredData.reduce((sum, book) => sum + parseFloat(book.total_recommendations || 0), 0);
                
                const avgWords = totalWords / filteredData.length;
                const avgRecs = totalRecs / filteredData.length;
                
                // 避免除以零
                const ratio = avgWords > 0 ? avgRecs / avgWords : 0;

                // 计算相关系数
                const xValues = filteredData.map(item => parseFloat(item.total_words || 0));
                const yValues = filteredData.map(item => parseFloat(item.total_recommendations || 0));
                
                // 检查数据是否有效
                const validData = xValues.every(x => !isNaN(x)) && yValues.every(y => !isNaN(y));
                
                let correlation = 0;
                if (validData) {
                    correlation = calculateCorrelation(xValues, yValues);
                }

                // 更新统计卡片，处理可能的NaN值
                document.getElementById('correlationValue').textContent = !isNaN(correlation) ? correlation.toFixed(3) : 'N/A';
                document.getElementById('avgWordsValue').textContent = !isNaN(avgWords) ? avgWords.toFixed(2) : 'N/A';
                document.getElementById('avgRecsValue').textContent = !isNaN(avgRecs) ? avgRecs.toFixed(2) : 'N/A';
                document.getElementById('ratioValue').textContent = !isNaN(ratio) ? ratio.toFixed(3) : 'N/A';
            } catch (error) {
                console.error("计算统计数据时出错:", error);
                document.getElementById('correlationValue').textContent = 'Error';
                document.getElementById('avgWordsValue').textContent = 'Error';
                document.getElementById('avgRecsValue').textContent = 'Error';
                document.getElementById('ratioValue').textContent = 'Error';
            }
        }

        // 下载图表
        document.getElementById('downloadBtn').addEventListener('click', function() {
            const link = document.createElement('a');
            link.download = '字数与推荐数关系.png';
            link.href = myChart.getDataURL({
                type: 'png',
                pixelRatio: 2,
                backgroundColor: '#fff'
            });
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        });

        // 类别筛选
        document.getElementById('categoryFilter').addEventListener('change', function() {
            const category = this.value;
            renderScatterChart(category);
            calculateStatistics(category);
        });

        // 刷新数据
        document.getElementById('refreshBtn').addEventListener('click', fetchChartData);

        // 响应窗口大小变化
        window.addEventListener('resize', function() {
            myChart.resize();
            trendChart.resize();
        });

        // 页面加载完成后获取数据
        document.addEventListener('DOMContentLoaded', fetchChartData);
    </script>
</body>
</html>